/* Copyright (c) 2015,2016 Lucky Byte, Inc.
 */
package com.lucky_byte.pay.jar.lualib;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.luaj.vm2.LuaTable;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.Varargs;
import org.luaj.vm2.lib.TwoArgFunction;
import org.luaj.vm2.lib.VarArgFunction;

import com.lucky_byte.pay.jar.FreeMarker;

/**
 * FreeMarker 处理接口
 */
public class LuaFreeMarker extends TwoArgFunction
{
	private static final Logger logger = LogManager.getLogger();

	@Override
	public LuaValue call(LuaValue modname, LuaValue env) {
		LuaValue library = tableOf();
		env.set("freemk", library);
		library.set("proc", new Processor());
		return library;
	}

	/**
	 * FTL 处理引擎
	 *
	 * 用法: proc(input_file, input_enc, output_file, output_enc, model)
	 */
	class Processor extends VarArgFunction
	{
		/**
		 * 检查 lua table 是否为数组
		 */
		private boolean isListTable(LuaTable table) {
			LuaValue key = LuaValue.NIL;
			while (true) {
				Varargs n = table.next(key);
				if ((key = n.arg1()).isnil()) {
					break;
				}
				if (!key.isinttype()) {
					return false;
				}
			}
			return true;
		}

		/**
		 * 将 lua table 转换为 list
		 */
		private void table2List(LuaTable table, List<Object> list) {
			LuaValue key = LuaValue.NIL;
			while (true) {
				Varargs n = table.next(key);
				if ((key = n.arg1()).isnil()) {
					break;
				}
				if (n.arg(2).istable()) {
					LuaTable new_table = n.checktable(2);
					if (this.isListTable(new_table)) {
						List<Object> new_list = new ArrayList<>();
						this.table2List(new_table, new_list);
						list.add(new_list);
					} else {
						Map<String, Object> new_map = new HashMap<>();
						this.table2Map(new_table, new_map);
						list.add(new_map);
					}
				} else {
					list.add(LuaHelper.value2Object(n.arg(2)));
				}
			}
		}

		/**
		 * 将 lua table 转换为 map
		 */
		private void table2Map(LuaTable table, Map<String, Object> map) {
			LuaValue key = LuaValue.NIL;
			while (true) {
				Varargs n = table.next(key);
				if ((key = n.arg1()).isnil()) {
					break;
				}
				if (n.arg(2).istable()) {
					LuaTable new_table = n.checktable(2);
					if (this.isListTable(new_table)) {
						List<Object> new_list = new ArrayList<>();
						this.table2List(new_table, new_list);
						map.put(key.checkjstring(), new_list);
					} else {
						Map<String, Object> new_map = new HashMap<>();
						this.table2Map(new_table, new_map);
						map.put(key.checkjstring(), new_map);
					}
				} else {
					map.put(key.checkjstring(),
							LuaHelper.value2Object(n.arg(2)));
				}
			}
		}

		@Override
		public Varargs invoke(Varargs args) {
			String in_path = args.checkjstring(1);
			String inenc = args.optjstring(2, "utf-8");
			String out_path = args.optjstring(3, null);
			String ouenc = args.optjstring(4, "utf-8");
			LuaTable table = args.opttable(5, null);

			Map<String, Object> model = new HashMap<>();
			if (table != null) {
				this.table2Map(table, model);
			}
			try {
				FreeMarker freemarker = FreeMarker.getInstance();
				File outfile = freemarker.generate(new File(in_path), inenc,
						new File(out_path), ouenc, model);
				if (outfile == null) {
					return LuaValue.NIL;
				}
				return LuaValue.valueOf(outfile.getAbsolutePath());
			} catch (Exception e) {
				logger.error("处理 FreeMarker 模板[{}]出错[{}].",
						in_path, e.getMessage());
				return LuaValue.NIL;
			}
		}
	}

}
